from querier.esquerier import ElasticSearchQuerier
import utils.utils as utils
import math

MINIMUM_SHOULD_MATCH = '5<85% 10<9'
WORDCLOUD_TOP_N = 100


class JDShopMatchQuerier(ElasticSearchQuerier):
    def __init__(self, es, index, doc_type, nlp_service):
        super(JDShopMatchQuerier, self).__init__(es, index, doc_type)
        self.nlp_service = nlp_service
        self.es = es
        self.index = index
        self.doc_type = doc_type

    def _build_query(self, args):
        """
        从args创建查询
        :param args:
        :return:
        """

        term = args.get('term', '')
        term = term if term else ''
        filters = args.get('filters', {})
        from_ = args.get('from', 0)
        size_ = args.get('size', 10)

        order = args.get('order_by', '')
        order = order if order else utils.ORDER_OVERALL
        highlight = args.get('highlight', False)
        highlight = highlight if highlight in (True, False) else False

        # 处理查询文本
        term2, keywords, ex_keywords, weights = utils.process_query_term(term, self.nlp_service)
        ex_kw = utils.get_kv_json(ex_keywords, weights)

        query = self._gen_query(term2, filters, order, from_, size_, highlight)
        return query, {}, {'keywords': keywords, 'order': order, 'ex_keywords': ex_kw}

    def _build_result(self, es_result, param):
        keywords = param['keywords']
        total = es_result['hits']['total']
        order = param['order']
        ex_keywords = param['ex_keywords']
        post = []
        for hit in es_result['hits']['hits']:
            post.append(extract_result(hit, order, keywords))
        return {
            'total': total,
            'ex_keywords': ex_keywords,
            'shop': post
        }

    @staticmethod
    def _gen_query(query_keywords, filters, order, from_, size_, highlight):
        query = {}
        filter_clause = []
        if filters:
            filter_clause = add_filter_clause(filter_clause, filters, 'shop_name')
            filter_clause = add_filter_clause(filter_clause, filters, 'shop_type')
            filter_clause = add_filter_range_clause(filter_clause, filters, 'shop_start_date')
            filter_clause = add_filter_range_clause(filter_clause, filters, 'shop_end_date')
            filter_clause = add_filter_range_clause(filter_clause, filters, 'commission_expense')
            filter_clause = add_filter_range_clause(filter_clause, filters, 'thirty_order_count')
            filter_clause = add_filter_range_clause(filter_clause, filters, 'avg_commission_ratio')
            filter_clause = add_filter_range_clause(filter_clause, filters, 'goods_score')
            filter_clause = add_filter_range_clause(filter_clause, filters, 'service_score')
            filter_clause = add_filter_range_clause(filter_clause, filters, 'effective_score')
            filter_clause = add_filter_range_clause(filter_clause, filters, 'star_score')

        if query_keywords:
            query['query'] = {
                'bool': {
                    'must': {
                        'bool': {
                            'should': [
                                {
                                    'match': {
                                        'categories': {
                                            'query': query_keywords,
                                            # 'minimum_should_match': MINIMUM_SHOULD_MATCH
                                        }
                                    }
                                },
                                {
                                    'match': {
                                        'shop_name': {
                                            'query': query_keywords,
                                            # 'minimum_should_match': MINIMUM_SHOULD_MATCH
                                        }
                                    }
                                },
                                {
                                    'query_string': {
                                        'query': query_keywords,
                                        'fields': ['shop_name', 'categories', 'keywords'],
                                        # 'minimum_should_match': MINIMUM_SHOULD_MATCH
                                    }
                                }
                            ]
                        }
                    },
                    'filter': filter_clause
                }
            }

        if order == 'star_score':
            query['sort'] = [
                {'star_score': 'desc'},
                '_score'
            ]

        elif order == 'avg_commission_ratio':
            query['sort'] = [
                {'avg_commission_ratio': 'desc'},
                '_score'
            ]

        elif order == 'thirty_order_count':
            query['sort'] = [
                {'thirty_order_count': 'desc'},
                '_score'
            ]
        elif order == 'commission_expense':
            query['sort'] = [
                {'commission_expense': 'desc'},
                '_score'
            ]
        else:
            query['boost_mode'] = 'replace'
            query['script_score'] = {
                "script": {
                    'lang': "painless",
                    'inline': "Math.log(doc['star_score'].value + 1.0)+Math.log(doc['avg_commission_ratio'].value+1.0)"
                              "/10.0+Math.log(doc['thirty_order_count'].value+1.0)"
                              "*Math.log(doc['commission_expense'].value+1.0)"
                }
            }
            query = {
                'query': {
                    'function_score': query
                },
                'sort': [
                    '_score',
                    {'star_score': 'desc'}
                ]
            }

        query['from'] = from_
        query['size'] = size_

        if highlight:
            query['highlight'] = {
                "pre_tags": ["<span class='keyword'>"],
                "post_tags": ["</span>"],
                "fields": {"keywords": {}, "shop_name": {}, "shop_type": {}}
            }

        return query


def add_filter_match(must_clause, filters, key):
    if key in filters:
        if filters[key]:
            clause = []
            must_clause.append({
                'bool': {'should': clause}
            })
            values = filters[key]
            if isinstance(values, str):
                values = values.split(' ')
            for fk in values:
                clause.append({'match': {key: {'query': fk, 'minimum_should_match': '10<85% 20<10'}}})
    return must_clause


def add_filter_clause(filter_clause, filters, key):
    if key in filters:
        if filters[key]:
            clause = []
            filter_clause.append({
                'bool': {
                    'must': clause
                }
            })
            for fk in filters[key]:
                clause.append({'term': {key: fk}})
    return filter_clause


def add_filter_range_clause(filter_clause, filters, key):
    if key in filters:
        if filters[key]:
            clause = []
            filter_clause.append({
                'bool': {
                    'must': clause
                }
            })
            fk = filters[key]
            if not isinstance(fk, list) or len(fk) < 1:
                pass
            else:
                min_fk = fk[0]
                if len(fk) >= 2:
                    max_fk = fk[1]
                else:
                    max_fk = None
                if min_fk is not None and min_fk != 'null':
                    clause.append({'range': {key: {"gte": min_fk}}})
                if max_fk is not None and max_fk != 'null':
                    clause.append({'range': {key: {"lte": max_fk}}})
    return filter_clause


def extract_result(hit, order, in_keywords):
    source_ = hit['_source']

    shop_id = source_['shop_id']
    shop_name = source_['shop_name']
    categories = source_['categories']
    thirty_order_count = source_['thirty_order_count']
    avg_commission_ratio = source_['avg_commission_ratio']
    shop_url = source_['shop_url']
    shop_type = source_['shop_type']
    cover_url = source_['cover_url']
    goods_score = source_['goods_score']
    service_score = source_['service_score']
    effective_score = source_['effective_score']
    star_score = source_['star_score']
    commission_expense = source_['commission_expense']
    shop_start_date = source_['shop_start_date']
    shop_end_date = source_['shop_end_date']
    cps_shop_url = source_['cps_shop_url']
    # keywords = source_['keywords'][0:WORDCLOUD_TOP_N]
    # weight = source_['weight'][0:WORDCLOUD_TOP_N]
    shop_influence_score = source_['star_score']
    created_at = source_['created_at']
    updated_at = source_['updated_at']
    # len_keywords = len(keywords)
    # len_weight = len(weight)
    # length = len_keywords if len_keywords <= len_weight else len_weight
    # ret = [{"text": keywords[i], "weight": weight[i]} for i in range(0, length)]

    score_ = hit['_score'] / (len(in_keywords) + 1)

    if order == utils.ORDER_RELATIVE:
        shop_relative = score_
    elif order == utils.ORDER_INFLUENCE:
        shop_relative = score_
    else:
        shop_relative = math.exp(score_ / (shop_influence_score + 1)) - 1.0

        # normalize shop_relative
        shop_relative = math.log2(1.0 + shop_relative) / 2
        shop_relative = min(shop_relative, 1.0)
    if len(in_keywords) == 0:
        shop_relative = 1.0

    keywords = source_['keywords'][0:10]
    h_keywords = keywords
    highlight = hit.get('highlight')
    if highlight:
        h_shop_name = highlight.get('shop_name')
        shop_name = shop_name if not h_shop_name else h_shop_name[0]
        h_shop_type = highlight.get('shop_type')
        shop_type = shop_type if not h_shop_type else h_shop_type[0]
        h_keywords = highlight.get('keywords')
        if h_keywords:
            hk2 = [s.replace("<span class='keyword'>", '').replace("</span>", '') for s in h_keywords]
            if hk2:
                h_keywords += [k for k in keywords if k not in hk2][0:10]

    return {
        'relative': shop_relative,
        'shop_id': shop_id,
        'shop_name': shop_name,
        'categories': categories,
        'thirty_order_count': thirty_order_count,
        'avg_commission_ratio': avg_commission_ratio,
        'shop_url': shop_url,
        'shop_type': shop_type,
        'cover_url': cover_url,
        'goods_score': goods_score,
        'service_score': service_score,
        'effective_score': effective_score,
        'star_score': star_score,
        'commission_expense': commission_expense,
        'shop_start_date': shop_start_date,
        'shop_end_date': shop_end_date,
        'cps_shop_url': cps_shop_url,
        'keywords': h_keywords if h_keywords else keywords[0:10],
        'created_at': created_at,
        'updated_at': updated_at
        # 'wordcloud': ret
    }
